﻿using System;
using System.Reflection;
using Zjh.PackSerializer.Attributes;

namespace Zjh.PackSerializer
{
    public class PackMember
    {
        public int Index => PackMemberAttribute.Index;
        public Type MemberType => Property.PropertyType;
        public PropertyInfo Property { get; }
        public PackMemberAttribute PackMemberAttribute { get; }

        protected IGetValue Getter { get; }
        protected ISetValue Setter { get; }

        private PackMember(PropertyInfo prop)
        {
            Property = prop;
            PackMemberAttribute = prop.GetCustomAttribute(typeof(PackMemberAttribute)) as PackMemberAttribute;

            Getter = CreateGetValueWrapper(prop);
            Setter = CreateSetValueWrapper(prop);
        }

        public void SetValue(object target, object value)
        {
            Setter?.Set(target, value);
        }

        public object GetValue(object target)
        {
            return Getter?.Get(target);
        }

        public static PackMember TryCreateInstance(PropertyInfo prop)
        {
            if (prop.GetCustomAttribute(typeof(PackMemberAttribute)) != null)
            {
                return new PackMember(prop);
            }
            return null;
        }

        private static ISetValue CreateSetValueWrapper(PropertyInfo prop)
        {
            Type instanceType = typeof(SetValueWrapper<,>).MakeGenericType(prop.DeclaringType, prop.PropertyType);
            return (ISetValue)Activator.CreateInstance(instanceType, prop);
        }

        private static IGetValue CreateGetValueWrapper(PropertyInfo prop)
        {
            Type instanceType = typeof(GetValueWrapper<,>).MakeGenericType(prop.DeclaringType, prop.PropertyType);
            return (IGetValue)Activator.CreateInstance(instanceType, prop);
        }

        public interface ISetValue
        {
            void Set(object target, object value);
        }

        public class SetValueWrapper<TTarget, TValue> : ISetValue
        {
            private Action<TTarget, TValue> setter;

            public SetValueWrapper(PropertyInfo prop)
            {
                if (prop.CanWrite == false)
                    throw new InvalidOperationException("属性不可写");
                setter = (Action<TTarget, TValue>)Delegate.CreateDelegate(typeof(Action<TTarget, TValue>), prop.SetMethod);
            }

            public void Set(TTarget target, TValue value)
            {
                setter.Invoke(target, value);
            }

            public void Set(object target, object value)
            {
                Set((TTarget)target, (TValue)value);
            }
        }

        public interface IGetValue
        {
            object Get(object target);
        }

        public class GetValueWrapper<TTarget, TValue> : IGetValue
        {
            private Func<TTarget, TValue> getter;

            public GetValueWrapper(PropertyInfo prop)
            {
                if (prop.CanRead == false)
                    throw new InvalidOperationException("属性不可读");
                getter = (Func<TTarget, TValue>)Delegate.CreateDelegate(typeof(Func<TTarget, TValue>), prop.GetMethod);
            }

            public TValue Get(TTarget target)
            {
                return getter.Invoke(target);
            }

            public object Get(object target)
            {
                return Get((TTarget)target);
            }
        }
    }
}